/*
 * Created on Jun 28, 2004
 */
package org.vectrics.user;

import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.log4j.Logger;
import org.vectrics.SystemException;
import org.vectrics.common.db.Auditable;
import org.vectrics.common.db.OwnedObject;
import org.vectrics.common.db.RecordObject;
import org.vectrics.common.util.DateSync;
import org.vectrics.cv.Experience;
import org.vectrics.cv.ExtraCertificate;
import org.vectrics.cv.PersonSkill;
import org.vectrics.cv.Studies;
import org.vectrics.domain.Domain;
import org.vectrics.location.Locale;
import org.vectrics.location.Location;
import org.vectrics.person.privilege.PersonPrivilegeHelper;
import org.vectrics.provider.CoreServiceLocator;

/**
 * Representa una persona en el sistema que puede ser tanto un empleado actual
 * como un candidato.
 * 
 * @author mmoore
 */
public class Person implements RecordObject, Auditable, Serializable, OwnedObject {

    public static final long serialVersionUID = 234232352355L;

    public static final Person NULL_PERSON = new Person("NULL_PERSON", "NULL_PERSON");

    public static final String PRIVILEGE_PERSON_VIEW = "person.view";

    public static String OBJECT_TYPE = "user";

    public static String EVENT_CREATED = "person.created";

    public static String EVENT_UPDATED = "person.updated";

    private String firstName = null;

    private String lastName = null;

    private String middleName;

    private String previousLastName = null;

    private ContactInfo contactInfo = null;

    private User user = null;

    private Long id;

    private Date dateOfBirth = null;

    private String title;

    private String knownAs;

    private String namePrefix;

    private String nameSuffix;

    private String gender;

    private String maritalStatus;

    private Locale locale = null;

    private Location birthCountry;

    private boolean voided = false;

    private Set privileges = new HashSet();

    private Set privilegeGroups = new HashSet();

    private Person createdBy = null;

    private Date createdDate = null;

    private Person lastModifiedBy = null;

    private Date lastModifiedDate = null;

    private Domain domain = null;

    private boolean systemUser = false;

    private Set<Studies> studies = new HashSet<Studies>();

    private Set<Experience> experiences = new HashSet<Experience>();

    private Set<PersonSkill> skills = new HashSet<PersonSkill>();

    private Set<ExtraCertificate> certificates = new HashSet<ExtraCertificate>();

    private String cuilOrCuit;

    private String emergencyContactName;

    private String emergencyContactPhoneNumer;

    private ProgrammignLanguageType programmingLanguage;

    private EnglishLevelType englishLevel;

    private static final transient Logger log = Logger.getLogger(Person.class);

    /**
     * @return the cuilOrCuit
     */
    public String getCuilOrCuit() {
        return cuilOrCuit;
    }

    /**
     * @param cuilOrCuit
     *            the cuilOrCuit to set
     */
    public void setCuilOrCuit(final String cuilOrCuit) {
        this.cuilOrCuit = cuilOrCuit;
    }

    /**
     * @return the emergencyContactName
     */
    public String getEmergencyContactName() {
        return emergencyContactName;
    }

    /**
     * @param emergencyContactName
     *            the emergencyContactName to set
     */
    public void setEmergencyContactName(final String emergencyContactName) {
        this.emergencyContactName = emergencyContactName;
    }

    /**
     * @return the emergencyContactPhoneNumer
     */
    public String getEmergencyContactPhoneNumer() {
        return emergencyContactPhoneNumer;
    }

    /**
     * @param emergencyContactPhoneNumer
     *            the emergencyContactPhoneNumer to set
     */
    public void setEmergencyContactPhoneNumer(final String emergencyContactPhoneNumer) {
        this.emergencyContactPhoneNumer = emergencyContactPhoneNumer;
    }

    /**
	 * 
	 */
    public Person() {
        super();
    }

    /**
	 * 
	 */
    public Person(final String lastName, final String firstName) {
        super();
        this.lastName = lastName;
        this.firstName = firstName;
    }

    /**
     * @return Returns the oBJECT_TYPE.
     */
    public static String getOBJECT_TYPE() {
        return OBJECT_TYPE;
    }

    /**
     * @param object_type
     *            The oBJECT_TYPE to set.
     */
    public static void setOBJECT_TYPE(final String object_type) {
        OBJECT_TYPE = object_type;
    }

    /**
     * @return Returns the systemUser.
     */
    public boolean getSystemUser() {
        return systemUser;
    }

    /**
     * @param systemUser
     *            The systemUser to set.
     */
    public void setSystemUser(final boolean systemUser) {
        this.systemUser = systemUser;
    }

    /**
     * @return Returns the contactInfo.
     */
    public ContactInfo getContactInfo() {
        return contactInfo;
    }

    /**
     * @param contactInfo
     *            The contactInfo to set.
     */
    public void setContactInfo(final ContactInfo contactInfo) {
        this.contactInfo = contactInfo;
    }

    public ContactInfo createContactInfoIfNotExist() {
        if (contactInfo == null) {
            contactInfo = new ContactInfo();
            setContactInfo(contactInfo);
            contactInfo.setPerson(this);
        }
        return contactInfo;
    }

    /*
     * public ContactInfo getPersonInfo() { return contactInfo; } public void
     * setPersonInfo(PersonInfo personInfo) { this.personInfo = personInfo; }
     * 
     * public PersonInfo createPersonInfoIfNotExist() { if (personInfo == null)
     * { personInfo = new PersonInfo(); this.setPersonInfo(personInfo);
     * personInfo.setPerson(this); } return(personInfo); }
     */
    /**
     * @return Returns the dateOfBirth.
     */
    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    /**
     * @param dateOfBirth
     *            The dateOfBirth to set.
     */
    public void setDateOfBirth(final Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    /**
     * @return Returns the firstName.
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * @param firstName
     *            The firstName to set.
     */
    public void setFirstName(final String firstName) {
        this.firstName = firstName;
    }

    /**
     * @return Returns the gender.
     */
    public String getGender() {
        return gender;
    }

    /**
     * @param gender
     *            The gender to set.
     */
    public void setGender(final String gender) {
        this.gender = gender;
    }

    /**
     * @return Returns the id.
     */
    public Long getId() {
        return id;
    }

    /**
     * @param id
     *            The id to set.
     */
    public void setId(final Long id) {
        this.id = id;
    }

    /**
     * @return Returns the knownAs.
     */
    public String getKnownAs() {
        return knownAs;
    }

    /**
     * @param knownAs
     *            The knownAs to set.
     */
    public void setKnownAs(final String knownAs) {
        this.knownAs = knownAs;
    }

    public int compareTo(final Object other) {
        int result = getId().compareTo(((Person) other).getId());
        return result;
    }

    /**
     * @return Returns the lastName.
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * @param lastName
     *            The lastName to set.
     */
    public void setLastName(final String lastName) {
        this.lastName = lastName;
    }

    /**
     * @return Returns the maritalStatus.
     */
    public String getMaritalStatus() {
        return maritalStatus;
    }

    /**
     * @param maritalStatus
     *            The maritalStatus to set.
     */
    public void setMaritalStatus(final String maritalStatus) {
        this.maritalStatus = maritalStatus;
    }

    /**
     * @return Returns the middleName.
     */
    public String getMiddleName() {
        return middleName;
    }

    /**
     * @param middleName
     *            The middleName to set.
     */
    public void setMiddleName(final String middleName) {
        this.middleName = middleName;
    }

    /**
     * @return Returns the name.
     */
    public String getName() {
        String name = "";
        if (lastName != null) {
            name += lastName;
        }
        if (firstName != null) {
            name += ", " + firstName;
        }
        if (middleName != null) {
            name += " " + middleName;
        }
        return name;
    }

    /**
     * @return Returns the previousLastName.
     */
    public String getPreviousLastName() {
        return previousLastName;
    }

    /**
     * @param previousLastName
     *            The previousLastName to set.
     */
    public void setPreviousLastName(final String previousLastName) {
        this.previousLastName = previousLastName;
    }

    /**
     * @return Returns the title.
     */
    public String getTitle() {
        return title;
    }

    /**
     * @param title
     *            The title to set.
     */
    public void setTitle(final String title) {
        this.title = title;
    }

    /**
     * @return Returns the user.
     */
    public User getUser() {
        return user;
    }

    /**
     * @param user
     *            The user to set.
     */
    public void setUser(final User user) {
        this.user = user;
    }

    public User createUserIfNotExist() {
        if (user == null) {
            user = new User();
            setUser(user);
            user.setPerson(this);
            user.setDomain(domain);
        }
        return user;
    }

    /**
     * @return Returns the namePrefix.
     */
    public String getNamePrefix() {
        return namePrefix;
    }

    /**
     * @param namePrefix
     *            The namePrefix to set.
     */
    public void setNamePrefix(final String namePrefix) {
        this.namePrefix = namePrefix;
    }

    /*
     * Add a role to this user
     */
    public void addPrivilege(final String userPrivilegeTypeCode, final Domain domain, final boolean applyToChildren) {
        UserPrivilegeType userPrivilegeType = CoreServiceLocator.getPersonService().findUserPrivilegeType(
                userPrivilegeTypeCode);
        this.addPrivilege(userPrivilegeType, domain, applyToChildren);
    }

    public void addPrivilege(final UserPrivilegeType userPrivilegeType, final Domain domain,
            final boolean applyToChildren) {
        PersonPrivilegeHelper helper = new PersonPrivilegeHelper(this);
        helper.addRole(userPrivilegeType, domain, applyToChildren);
    }

    public void removePrivilege(final UserPrivilegeType userPrivilegeType, final Domain domain) {
        log.debug("removeRole() called");
        if (privileges != null) {
            UserPrivilege userPrivilege = this.findPrivilege(userPrivilegeType, domain);
            if (userPrivilege != null) {
                privileges.remove(userPrivilege);
            }
        } else {
            log.error("removeRole() called, by roles were never populated");
        }
    }

    public void clearPrivileges() {
        if (privileges != null) {
            privileges.clear();
        } else {
            log.error("clearRoles() called, by roles were never populated");
        }
    }

    public UserPrivilege findPrivilege(final String roleCode, final Domain domain) {
        PersonPrivilegeHelper helper = new PersonPrivilegeHelper(this);
        return helper.findPrivilege(roleCode, domain);
    }

    public UserPrivilege findPrivilege(final UserPrivilegeType userPrivilegeType, final Domain domain) {
        if (domain == null)
            throw new SystemException("passed domain is null");

        PersonPrivilegeHelper helper = new PersonPrivilegeHelper(this);
        return helper.findRole(userPrivilegeType, domain);
    }

    public boolean hasPrivilege(final String userPrivilegeTypeCode, final Domain domain) {
        if (domain == null)
            throw new SystemException("passed domain is null");
        PersonPrivilegeHelper helper = new PersonPrivilegeHelper(this);
        return helper.hasRole(userPrivilegeTypeCode, domain);
    }

    public boolean hasPrivilege(final UserPrivilegeType userPrivilegeType, final Domain domain) {
        if (domain == null)
            throw new SystemException("passed domain is null");

        UserPrivilege foundRole = this.findPrivilege(userPrivilegeType, domain);
        return foundRole != null;
    }

    public boolean hasDomainOrGlobalPrivilege(final UserPrivilegeType neededRole, final Domain domain) {
        if (domain == null)
            throw new SystemException("passed domain is null");
        return hasDomainOrGlobalRole(neededRole.getCode(), domain);
    }

    public boolean hasDomainOrGlobalRole(final String neededRole, final Domain domain) {
        if (domain == null)
            throw new SystemException("passed domain is null");
        PersonPrivilegeHelper helper = new PersonPrivilegeHelper(this);
        return helper.hasDomainOrGlobalRole(neededRole, domain);
    }

    /**
     * @return Returns the roles.
     */
    public Set getPrivileges() {
        return privileges;
    }

    /**
     * @param roles
     *            The roles to set.
     */
    public void setPrivileges(final Set privileges) {
        this.privileges = privileges;
    }

    /**
     * @return Returns the locale.
     */
    public Locale getLocale() {
        return locale;
    }

    /**
     * @param locale
     *            The locale to set.
     */
    public void setLocale(final Locale locale) {
        this.locale = locale;
    }

    public void setLocale(final String code) {
        Locale locale = CoreServiceLocator.getLocationService().findLocale(code);
        if (locale == null)
            throw new SystemException("Could not find locale: " + code);
        this.setLocale(locale);
    }

    public void setLocale(final java.util.Locale locale) {
        Locale vectricsLocale = CoreServiceLocator.getLocationService().findLocale(locale.getLanguage());
        this.setLocale(vectricsLocale);
    }

    /**
     * @return Returns the domain.
     */
    public Domain getDomain() {
        return domain;
    }

    /**
     * @param domain
     *            The domain to set.
     */
    public void setDomain(final Domain domain) {
        if (domain == null)
            throw new SystemException("Domain is null");
        this.domain = domain;
    }

    /**
     * @return Returns the voided.
     */
    public boolean getVoided() {
        return voided;
    }

    /**
     * @param voided
     *            The voided to set.
     */
    public void setVoided(final boolean voided) {
        this.voided = voided;
    }

    /**
     * @return Returns the createdBy.
     */
    public Person getCreatedBy() {
        return createdBy;
    }

    /**
     * @param createdBy
     *            The createdBy to set.
     */
    public void setCreatedBy(final Person createdBy) {
        this.createdBy = createdBy;
    }

    /**
     * @return Returns the createdDate.
     */
    public Date getCreatedDate() {
        return createdDate;
    }

    /**
     * @param createdDate
     *            The createdDate to set.
     */
    public void setCreatedDate(final Date createdDate) {
        this.createdDate = createdDate;
    }

    /**
     * @return Returns the lastModifiedBy.
     */
    public Person getLastModifiedBy() {
        return lastModifiedBy;
    }

    /**
     * @param lastModifiedBy
     *            The lastModifiedBy to set.
     */
    public void setLastModifiedBy(final Person lastModifiedBy) {
        this.lastModifiedBy = lastModifiedBy;
    }

    /**
     * @return Returns the lastModifiedDate.
     */
    public Date getLastModifiedDate() {
        return lastModifiedDate;
    }

    /**
     * @param lastModifiedDate
     *            The lastModifiedDate to set.
     */
    public void setLastModifiedDate(final Date lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(getFullName()).append(getCuilOrCuit()).toHashCode();
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        // if (!obj.getClass().isAssignableFrom(getClass())) {
        // return false;
        // }
        Person otherPerson = (Person) obj;
        return new EqualsBuilder().append(getFullName(), otherPerson.getFullName())
                .append(getCuilOrCuit(), otherPerson.getCuilOrCuit()).isEquals();
    }

    /**
     * @return Returns the privilegeGroups.
     */
    public Set getPrivilegeGroups() {
        return privilegeGroups;
    }

    /**
     * @param privilegeGroups
     *            The privilegeGroups to set.
     */
    public void setPrivilegeGroups(final Set privilegeGroups) {
        this.privilegeGroups = privilegeGroups;
    }

    public PersonPrivilegeGroup addPrivilegeGroup(final PersonPrivilegeGroupType type, final Domain domain,
            final boolean global) {
        if (domain == null)
            throw new SystemException("addPrivilegeGroup() was passed a null domain - needs to be non-null.");
        PersonPrivilegeGroup group = findPrivilegeGroup(type, domain);

        if (hasGroupMembership(type, domain) == false) {
            group = findPrivilegeGroup(type, domain);
            if (group == null) {
                group = new PersonPrivilegeGroup();
                group.setDomain(domain);
                group.setPerson(this);
                group.setDateStart(DateSync.getCurrentTime());
                group.setPersonPrivilegeGroupType(type);
                group.setGlobal(global);
                privilegeGroups.add(group);
            }
            if (group != null) {
                group.setGlobal(global);
            }
            group.setVoided(false);
        } else if (group != null) {
            group.setVoided(false);
        }
        return group;
    }

    public void removePrivilegeGroup(final PersonPrivilegeGroupType type, final Domain domain) {
        PersonPrivilegeGroup group = findPrivilegeGroup(type, domain);
        if (group != null) {
            group.setVoided(true);
        }
    }

    public boolean hasDomainOrGlobalMembership(final PersonPrivilegeGroupType type, final Domain domain) {
        boolean result = hasGroupMembership(type, domain);
        if (result == false) {
            result = hasGroupMembership(type, null);
        }
        return result;
    }

    public boolean hasGroupMembership(final PersonPrivilegeGroupType type, final Domain domain) {
        PersonPrivilegeGroup group = findPrivilegeGroup(type, domain);
        return group != null && group.getVoided() == false;
    }

    public PersonPrivilegeGroup findPrivilegeGroup(final PersonPrivilegeGroupType type, final Domain domain) {
        PersonPrivilegeGroup found = null;
        Iterator iter = privilegeGroups.iterator();
        while (iter.hasNext()) {
            PersonPrivilegeGroup privGroup = (PersonPrivilegeGroup) iter.next();
            if (privGroup.getPersonPrivilegeGroupType().getCode().equals(type.getCode())) {
                if (privGroup.getDomain() == null) {
                    if (domain == null) {
                        found = privGroup;
                    }
                } else {
                    if (domain != null) {
                        if (domain.getId().equals(privGroup.getDomain().getId())) {
                            found = privGroup;
                        }
                    }
                }
            }
        }
        return found;
    }

    /*
     * public boolean hasRightToAssignGroup(PersonPrivilegeGroupType groupType,
     * Company company, boolean global) { boolean result = false;
     * 
     * //Set allPrivilegeGroupAssignRights =
     * CoreServiceLocator.getPersonService(
     * ).findAllPrivilegeGroupAssignRights(); Iterator iter =
     * this.getPrivileges().iterator(); while (iter.hasNext()) { UserPrivilege
     * userPrivilege = (UserPrivilege)iter.next(); UserPrivilegeType
     * userPrivilegeType = userPrivilege.getPrivilegeType(); result =
     * CoreServiceLocator
     * .getPersonService().doesRoleHaveRightToAssignGroup(userPrivilegeType,
     * groupType); } return(result); }
     */

    @Override
    public String toString() {
        String idStr = null;
        if (id != null) {
            idStr = id.toString();
        }
        return "{" + idStr + "|" + lastName + "|" + firstName + "|" + middleName + "}";
    }

    public boolean logEntityChanges() {
        return true;
    }

    public Set findDomainsForPrivilege(final String privCode) {
        Set domains = new HashSet();
        Iterator iter = getPrivileges().iterator();
        while (iter.hasNext()) {
            UserPrivilege privilege = (UserPrivilege) iter.next();
            if (privilege.getPrivilegeType().getCode().equals(privCode)) {
                domains.add(privilege.getDomain());
                if (privilege.getGlobal()) {
                    domains.addAll(privilege.getDomain().retrieveChildrenInBranch());
                }
            }
        }
        return domains;
    }

    public String getNameSuffix() {
        return nameSuffix;
    }

    public void setNameSuffix(final String nameSuffix) {
        this.nameSuffix = nameSuffix;
    }

    public String getFullName() {
        String fullName = "";

        if (firstName != null) {
            if (fullName.length() > 0) {
                fullName += " ";
            }
            fullName += firstName;
        }
        if (middleName != null) {
            fullName += " " + middleName;
        }
        if (lastName != null) {
            fullName += " " + lastName;
        }
        return fullName;
    }

    public Set<Studies> getStudies() {
        return studies;
    }

    public void setStudies(final Set<Studies> studies) {
        this.studies = studies;
    }

    /**
     * @return the experiences
     */
    public Set<Experience> getExperiences() {
        return experiences;
    }

    /**
     * @param experiences
     *            the experiences to set
     */
    public void setExperiences(final Set<Experience> experiences) {
        this.experiences = experiences;
    }

    public Set<PersonSkill> getSkills() {
        return skills;
    }

    public void setSkills(final Set<PersonSkill> skills) {
        this.skills = skills;
    }

    public Location getBirthCountry() {
        return birthCountry;
    }

    public void setBirthCountry(final Location birthCountry) {
        this.birthCountry = birthCountry;
    }

    public ProgrammignLanguageType getProgrammingLanguage() {
        return programmingLanguage;
    }

    public void setProgrammingLanguage(final ProgrammignLanguageType programingLanguage) {
        programmingLanguage = programingLanguage;
    }

    public EnglishLevelType getEnglishLevel() {
        return englishLevel;
    }

    public void setEnglishLevel(final EnglishLevelType englishLevel) {
        this.englishLevel = englishLevel;
    }

    /**
     * M�todo que retorna la cadena de caracteres correspondiente al g�nero de
     * la persona.
     */
    public String getGenderDescription(final java.util.Locale locale) {
        String genderDescription = null;

        if (getGender().equals("m")) {
            genderDescription = CoreServiceLocator.getPhraseProvider().getMessage("prompt.male", locale);
        } else {
            genderDescription = CoreServiceLocator.getPhraseProvider().getMessage("prompt.female", locale);
        }
        return genderDescription;
    }

    /**
     * M�todo que retorna la cadena de caracteres correspondiente al estado
     * civil de la persona.
     */
    public String getMaritalStatusDescription(final java.util.Locale locale) {
        String maritalStatusDescription = null;

        if (getMaritalStatus().equals("")) {
            maritalStatusDescription = CoreServiceLocator.getPhraseProvider().getMessage("prompt.not.given", locale);
        } else {
            if (getMaritalStatus().equals("s")) {
                maritalStatusDescription = CoreServiceLocator.getPhraseProvider().getMessage("prompt.not.married",
                        locale);
            } else {
                if (getMaritalStatus().equals("m")) {
                    maritalStatusDescription = CoreServiceLocator.getPhraseProvider().getMessage("prompt.married",
                            locale);
                } else {
                    maritalStatusDescription = CoreServiceLocator.getPhraseProvider().getMessage("prompt.divorced",
                            locale);
                }
            }
        }
        return maritalStatusDescription;
    }

    /**
     * @return the certificates
     */
    public Set<ExtraCertificate> getCertificates() {
        return certificates;
    }

    /**
     * @param certificates
     *            the certificates to set
     */
    public void setCertificates(final Set<ExtraCertificate> certificates) {
        this.certificates = certificates;
    }
}
